package org.wupgame.service.impl;

import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.wupgame.model.Card;
import org.wupgame.model.Player;
import org.wupgame.model.RoomState;
import org.wupgame.model.e.*;
import org.wupgame.model.e.Number;
import org.wupgame.service.GameRule;
import org.wupgame.service.e.GameException;
import org.wupgame.util.RedisLockUtil;
import org.wupgame.util.RedisUtil;

import java.util.*;

@Service
public class GameRuleImpl implements GameRule {
	
	@Resource
	private RedisUtil redisUtil;
	
	@Resource
	private RedisLockUtil redisLockUtil;
	
	/**
	 * 玩家出牌的主要方法
	 *
	 * @param roomId      房间ID
	 * @param playerId    玩家ID
	 * @param cardId      要出的卡牌ID
	 * @param chosenColor 选择的颜色（用于万能牌）
	 * @param isChallenge 是否进行挑战（针对+4牌）
	 *
	 * @return 出牌是否成功
	 * @throws GameException 当出现游戏异常时抛出
	 */
	@Override
	@Transactional
	public boolean playCard(
		String roomId,
		Long playerId,
		String cardId,
		Color chosenColor,
		boolean isChallenge) {
		// 获取分布式锁，防止并发操作导致数据不一致
		String lockKey = "game_lock:" + roomId;
		if (! redisLockUtil.tryLock(lockKey)) {
			throw new GameException("无法获取游戏锁，请稍后再试");
		}
		
		try {
			// 从Redis中获取房间的当前状态
			RoomState roomState = getRoomState(roomId);
			
			if (roomState.isEnd()) {
				throw new GameException("游戏已结束");
			}
			
			// 验证是否为当前玩家的回合，如果不是则抛出异常
			if (! isPlayerTurn(
				roomState,
				playerId)) {
				throw new GameException("不是你的回合");
			}
			
			// 根据玩家ID获取玩家对象，并根据卡牌ID获取玩家手中的卡牌
			Player player = getPlayerById(
				roomState,
				playerId);
			if (player == null) {
				throw new GameException("无法找到指定的玩家");
			}
			Card card = getCardFromPlayerHand(
				player,
				cardId);
			
			// 验证玩家要出的牌是否符合游戏规则
			if (roomState.getTurnCount() != 0) {
				if (! isValidPlay(
					roomState,
					card,
					player)) {
					throw new GameException("非法出牌");
				}
			}
			
			// 如果找到了这张牌，则应用卡牌效果，并将索引转移至下一位玩家
			if (card != null) {
				applyCardEffect(
					roomState,
					card,
					chosenColor);
			} else {
				// 如果未找到指定的卡牌，抛出异常
				throw new GameException("无法找到指定的牌");
			}
			
			// 从出牌玩家手中移除卡牌
			removeCardFromHand(
				player,
				cardId);
			// 移除手牌后出牌阶段完成，回合数+1以便下回合进行合法性验证
			roomState.setTurnCount(roomState.getTurnCount() + 1);
			
			// 同时检查玩家手牌是否为空
			if (checkGameEnd(player)) {
				roomState.setEnd(true);
				countScore(
					roomState,
					player);
			}
			
			// 将更新后的房间状态保存到Redis中
			updateRoomState(roomState);
			
			// 返回出牌成功的标志
			return true;
		}
		finally {
			// 无论出牌成功与否，都要释放分布式锁，确保其他用户可以继续操作
			redisLockUtil.unlock(lockKey);
		}
	}
	
	/**
	 * 计算游戏结束时玩家的得分
	 * <p>
	 * 游戏规则： 1. 胜利玩家获得所有其他玩家手中剩余卡牌的分数总和 2. 其他玩家失去他们手中卡牌对应分数
	 * <p>
	 * 计分规则： - 数字牌：按牌面数字计分（0-9分） - 功能牌（跳过、反转、+2）：每张20分 - 万能牌（变色、+4）：每张50分
	 *
	 * @param roomState    房间状态对象，包含所有玩家信息
	 * @param winnerPlayer 获胜的玩家对象
	 */
	private void countScore(
		RoomState roomState,
		Player winnerPlayer) {
		int winnerScore = 0;
		// 遍历房间中的每个玩家，计算他们的得分(包括胜者)
		for (Player player : roomState.getPlayers()) {
			// 已经确定能执行if方法的并非胜者
			if (! Objects.equals(
				player.getId(),
				winnerPlayer.getId())) {
				int sum = 0;
				for (Card card : player.getCards()) {
					// 处理数字牌，按牌面赋分
					if (card
						.getType()
						.equals(Type.NUMBER)) {
						sum += card
							.getNumber()
							.ordinal();
					}
					// 处理颜色功能牌，每张颜色功能牌20分
					if (card
						    .getType()
						    .equals(Type.SKIP) || card
						    .getType()
						    .equals(Type.REVERSE) || card
						    .getType()
						    .equals(Type.DRAW_2)) {
						sum += 20;
					}
					// 处理黑色功能牌，每张黑色功能牌50分
					if (card
						    .getType()
						    .equals(Type.WILD) || card
						    .getType()
						    .equals(Type.WILD_DRAW_4)) {
						sum += 50;
					}
				}
				// 胜者不用计算自己的分数
				player.setScore(- sum);
				winnerScore += sum;
			}
		}
		// 最后所有败者分数加给胜者
		winnerPlayer.setScore(winnerScore);
	}
	
	/**
	 * 从玩家手牌中移除指定卡牌 根据卡牌ID从玩家的手牌列表中找到并移除对应的卡牌
	 *
	 * @param player 玩家对象
	 * @param cardId 要移除的卡牌ID
	 */
	private void removeCardFromHand(
		Player player,
		String cardId) {
		// 使用removeIf方法移除符合条件的卡牌
		// 条件是卡牌的ID与传入的cardId相等
		player
			.getCards()
			.removeIf(
				card -> card
					.getId()
					.equals(cardId));
	}
	
	/**
	 * 获取房间状态 从Redis缓存中获取指定房间的当前状态
	 *
	 * @param roomId 房间ID
	 *
	 * @return 房间状态对象，如果未找到则返回null
	 */
	private RoomState getRoomState(String roomId) {
		// 从Redis缓存中获取房间状态
		return (RoomState) redisUtil.get(roomId);
	}
	
	/**
	 * 更新房间状态 将房间状态保存到Redis中，以便其他用户可以获取最新的游戏状态
	 *
	 * @param roomState 要更新的房间状态对象
	 */
	private void updateRoomState(RoomState roomState) {
		redisUtil.set(
			roomState.getId(),
			roomState);
	}
	
	/**
	 * 从玩家手牌中获取指定ID的卡牌
	 *
	 * @param player 玩家对象
	 * @param cardId 要查找的卡牌ID
	 *
	 * @return 找到的卡牌对象，如果未找到则返回null
	 */
	private Card getCardFromPlayerHand(
		Player player,
		String cardId) {
		// 获取玩家的所有手牌
		LinkedHashSet<Card> cards = player.getCards();
		// 遍历手牌查找指定ID的卡牌
		for (Card card : cards) {
			// 使用Objects.equals安全地比较卡牌ID
			if (Objects.equals(
				card.getId(),
				cardId)) {
				// 找到匹配的卡牌，返回该卡牌对象
				return card;
			}
		}
		// 未找到指定ID的卡牌，返回null
		return null;
	}
	
	/**
	 * 根据玩家ID从房间状态中获取玩家对象
	 *
	 * @param roomState 房间状态对象
	 * @param playerId  要查找的玩家ID
	 *
	 * @return 找到的玩家对象，如果未找到则返回null
	 */
	private Player getPlayerById(
		RoomState roomState,
		Long playerId) {
		// 获取房间中的所有玩家
		List<Player> players = roomState.getPlayers();
		// 遍历玩家列表查找指定ID的玩家
		for (Player player : players) {
			// 使用Objects.equals安全地比较玩家ID
			if (Objects.equals(
				player.getId(),
				playerId)) {
				// 找到匹配的玩家，返回该玩家对象
				return player;
			}
		}
		// 未找到指定ID的玩家，返回null
		return null;
	}
	
	/**
	 * 判断当前是否轮到指定玩家行动
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 * @param playerId  玩家ID
	 *
	 * @return 如果轮到该玩家返回true，否则返回false
	 */
	private boolean isPlayerTurn(
		RoomState roomState,
		Long playerId) {
		Player currentPlayer = roomState
			.getPlayers()
			.get(roomState.getTurnIndex());
		return Objects.equals(
			currentPlayer.getId(),
			playerId);
	}
	
	/**
	 * 验证玩家的出牌是否符合游戏规则
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 * @param card      玩家想要打出的牌
	 * @param player    当前玩家
	 *
	 * @return 如果出牌合法返回true，否则返回false
	 */
	private boolean isValidPlay(
		RoomState roomState,
		Card card,
		Player player) {
		Card lastCard = roomState.getLastCard();
		
		// 如果有待抽牌，只能出相同类型的抽牌
		// 不同于村规，该规则变色牌不能跳加牌
		if (roomState.getPendingDraw() > 0) {
			return canStackDrawCard(
				roomState,
				card);
		}
		
		// 万能牌除了最后一张以外总是可以出
		if (card.getType() == Type.WILD || card.getType() == Type.WILD_DRAW_4 && player
			                                                                         .getCards()
			                                                                         .size() > 1) {
			return true;
		}
		
		// 颜色相同
		if (card.getColor() == lastCard.getColor()) {
			return true;
		}
		
		// 类型相同（非数字牌）
		if (card.getType() != Type.NUMBER && card.getType() == lastCard.getType()) {
			return true;
		}
		
		// 数字相同
		return card.getType() == Type.NUMBER && lastCard.getType() == Type.NUMBER
		       && card.getNumber() == lastCard.getNumber();
	}
	
	/**
	 * 判断玩家是否可以叠加抽牌（+2或+4） 当上一张牌是抽牌时，玩家只能出相同类型或更高级别的抽牌来叠加
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 * @param card      玩家想要打出的牌
	 *
	 * @return 如果可以叠加抽牌返回true，否则返回false
	 */
	private boolean canStackDrawCard(
		RoomState roomState,
		Card card) {
		Card lastCard = roomState.getLastCard();
		// 如果上一张牌是+2牌
		if (lastCard
			.getType()
			.equals(Type.DRAW_2)) {
			// 当前牌可以是+2或+4来叠加
			return card
				       .getType()
				       .equals(Type.DRAW_2) || card
				       .getType()
				       .equals(Type.WILD_DRAW_4);
		}
		// 如果上一张牌是+4牌
		if (lastCard
			.getType()
			.equals(Type.WILD_DRAW_4)) {
			// 当前牌只能是+4来叠加
			return card
				.getType()
				.equals(Type.WILD_DRAW_4);
		}
		// 其他情况不能叠加抽牌
		return false;
	}
	
	/**
	 * 应用卡牌的效果到游戏状态中 根据卡牌类型执行相应的游戏逻辑，如跳过、反转、抽牌等
	 *
	 * @param roomState   房间状态对象，包含游戏的当前状态信息
	 * @param card        要应用效果的卡牌
	 * @param chosenColor 玩家选择的颜色（用于万能牌）
	 */
	private void applyCardEffect(
		RoomState roomState,
		Card card,
		Color chosenColor) {
		switch (card.getType()) {
			case NUMBER:
				// 普通数字牌，切换到下一个玩家
				moveToNextPlayer(roomState);
				break;
			
			case SKIP:
				// 跳过下一个玩家
				skipNextPlayer(roomState);
				break;
			
			case REVERSE:
				// 反转方向
				reverseDirection(roomState);
				// 如果只有两个玩家，反转相当于跳过
				if (roomState
					    .getPlayers()
					    .size() == 2) {
					skipNextPlayer(roomState);
				} else {
					moveToNextPlayer(roomState);
				}
				break;
			
			case DRAW_2:
				// 设置待抽牌数
				// 如果已经有待抽牌且类型匹配，则累加
				if (roomState.getPendingDraw() > 0 && roomState.getDrawType() == DrawType.DRAW_2) {
					roomState.setPendingDraw(roomState.getPendingDraw() + 2);
				} else {
					// 否则设置新的抽牌数
					roomState.setPendingDraw(2);
					roomState.setDrawType(DrawType.DRAW_2);
				}
				moveToNextPlayer(roomState);
				break;
			
			case WILD:
				// 选择颜色
				if (chosenColor == null) {
					throw new GameException("万能牌必须选择颜色");
				}
				card.setColor(chosenColor);
				moveToNextPlayer(roomState);
				break;
			
			case WILD_DRAW_4:
				// 选择颜色并设置抽牌
				if (chosenColor == null) {
					throw new GameException("万能牌必须选择颜色");
				}
				card.setColor(chosenColor);
				
				// 如果已经有待抽牌且类型匹配，则累加
				if (roomState.getPendingDraw() > 0 && roomState.getDrawType() == DrawType.WILD_DRAW_4 || roomState.getDrawType() == DrawType.DRAW_2) {
					roomState.setPendingDraw(roomState.getPendingDraw() + 4);
				} else {
					// 否则设置新的抽牌数
					roomState.setPendingDraw(4);
					roomState.setDrawType(DrawType.WILD_DRAW_4);
				}
				
				// 标记可以质疑
				if (hasConfig(
					roomState,
					Config.CHALLENGE)) {
					roomState.setAwaitingChallenge(true);
					roomState.setLastWildDrawPlayer(getCurrentPlayer(roomState).getId());
				}
				
				moveToNextPlayer(roomState);
				break;
		}
		// 更新最后打出的牌
		roomState.setLastCard(card);
	}
	
	
	/**
	 * 获取当前回合的玩家
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 *
	 * @return 当前回合的玩家对象
	 */
	private Player getCurrentPlayer(RoomState roomState) {
		return roomState
			.getPlayers()
			.get(roomState.getTurnIndex());
	}
	
	
	/**
	 * 检查房间是否具有指定的配置项
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 * @param config    要检查的配置项
	 *
	 * @return 如果房间具有指定配置返回true，否则返回false
	 */
	private boolean hasConfig(
		RoomState roomState,
		Config config) {
		ArrayList<Config> configs = roomState.getConfigs();
		// 遍历所有配置项，检查是否存在指定的配置
		for (Config c : configs) {
			if (c == config) {
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * 反转游戏进行方向 将当前方向从顺时针改为逆时针，或从逆时针改为顺时针
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 */
	private void reverseDirection(RoomState roomState) {
		// 判断当前方向，如果是顺时针则改为逆时针，否则改为顺时针
		roomState.setDirection(
			roomState.getDirection() == Direction.CLOCKWISE ? Direction.COUNTER_CLOCKWISE : Direction.CLOCKWISE);
	}
	
	
	/**
	 * 跳过下一个玩家的回合 根据当前游戏方向，将回合索引向前移动两位，实现跳过一个玩家的效果
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 */
	private void skipNextPlayer(RoomState roomState) {
		// 计算新的回合索引：
		// 1. 如果是顺时针方向，索引增加2；如果是逆时针方向，索引减少2
		// 2. 加上玩家总数是为了处理负数情况（避免出现负数索引）
		// 3. 最后对玩家总数取模，确保索引在有效范围内
		roomState.setTurnIndex(
			(
				roomState.getTurnIndex() + (roomState.getDirection() == Direction.CLOCKWISE ? 2 : - 2) + roomState
					.getPlayers()
					.size())
			% roomState
				.getPlayers()
				.size());
	}
	
	
	/**
	 * 移动到下一个玩家 根据当前游戏方向，将回合索引向前或向后移动一位
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 */
	@Override
	public void moveToNextPlayer(RoomState roomState) {
		// 计算新的回合索引：
		// 1. 如果是顺时针方向，索引增加1；如果是逆时针方向，索引减少1
		// 2. 加上玩家总数是为了处理负数情况（避免出现负数索引）
		// 3. 最后对玩家总数取模，确保索引在有效范围内
		roomState.setTurnIndex(
			(
				roomState.getTurnIndex() + (roomState.getDirection() == Direction.CLOCKWISE ? 1 : - 1) + roomState
					.getPlayers()
					.size())
			% roomState
				.getPlayers()
				.size());
	}
	
	
	/**
	 * 玩家抽牌操作 处理玩家的抽牌请求，包括正常抽牌和因被加牌而必须抽牌的情况
	 *
	 * @param roomId      房间ID
	 * @param playerId    玩家ID
	 * @param isChallenge 是否选择质疑（针对+4牌）
	 *
	 * @return 抽牌操作是否成功
	 */
	@Override
	@Transactional
	public boolean drawCard(
		String roomId,
		Long playerId,
		boolean isChallenge) {
		// 使用Redis锁确保并发安全
		String lockKey = "game_lock:" + roomId;
		if (! redisLockUtil.tryLock(lockKey)) {
			throw new GameException("无法获取游戏锁，请稍后再试");
		}
		
		try {
			RoomState roomState = getRoomState(roomId);
			
			// 验证是否为当前玩家回合
			if (! isPlayerTurn(
				roomState,
				playerId)) {
				throw new GameException("不是你的回合");
			}
			
			Player player = getPlayerById(
				roomState,
				playerId);
			
			if (player == null) {
				throw new GameException("无法找到指定的玩家");
			}
			
			// 如果有待抽牌数，处理抽牌
			if (roomState.getPendingDraw() > 0) {
				// 处理质疑逻辑
				// 如果当前加牌类型是+4牌，且规则里面允许质疑，且房间处于可质疑状态，且当前玩家选择质疑
				if (roomState
					    .getDrawType()
					    .equals(DrawType.WILD_DRAW_4) && hasConfig(
					roomState,
					Config.CHALLENGE) && roomState.isAwaitingChallenge()) {
					// 确保修改的是根源的RoomState对象，而不是副本
					roomState = challengeWildDraw4(
						roomState,
						playerId,
						isChallenge);
				} else {
					// 抽取待抽牌数
					for (
						int i = 0;
						i < roomState.getPendingDraw();
						i++) {
						drawCardFromDeck(
							roomState,
							player);
					}
					
					// 清空待抽牌
					roomState.setPendingDraw(0);
					roomState.setDrawType(DrawType.NONE);
				}
			} else {
				// 正常抽一张牌
				drawCardFromDeck(
					roomState,
					player);
				
				// 如果配置允许抽牌后出牌
				if (hasConfig(
					roomState,
					Config.IMMEDIATE)) {
					// 不切换玩家，给玩家一次出牌机会
					return true;
				}
			}
			
			// 切换到下一个玩家
			moveToNextPlayer(roomState);
			
			// 更新房间状态
			updateRoomState(roomState);
			
			return true;
		}
		finally {
			// 释放Redis锁
			redisLockUtil.unlock(lockKey);
		}
	}
	
	
	/**
	 * 从牌堆中抽取一张牌给玩家 将牌堆顶的牌移除并添加到玩家手牌中
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 * @param player    要抽牌的玩家
	 */
	private void drawCardFromDeck(
		RoomState roomState,
		Player player) {
		// 将牌堆顶的牌添加到玩家手牌末尾
		player
			.getCards()
			.addLast(roomState
				         .getDeck()
				         .getLast());
		// 从牌堆中移除已抽取的牌
		roomState
			.getDeck()
			.removeLast();
	}
	
	
	/**
	 * 质疑+4牌的操作 当玩家被+4牌时，可以选择质疑出牌玩家是否真的没有其他可出的牌
	 *
	 * @param roomState    房间状态数据信息
	 * @param challengerId 质疑者的玩家ID
	 * @param isChallenge  是否选择质疑
	 *
	 * @return 质疑操作是否成功
	 */
	@Override
	@Transactional
	public RoomState challengeWildDraw4(
		RoomState roomState,
		Long challengerId,
		boolean isChallenge) {
		
		// 验证是否允许质疑
		if (! roomState.isAwaitingChallenge()) {
			throw new GameException("当前不能质疑");
		}
		
		// 验证是否为当前玩家
		if (! isPlayerTurn(
			roomState,
			challengerId)) {
			throw new GameException("不是你的回合");
		}
		
		// 当前玩家是否质疑
		if (isChallenge) {
			// 获取出+4的玩家
			Long wildDrawPlayerId = roomState.getLastWildDrawPlayer();
			Player wildDrawPlayer = getPlayerById(
				roomState,
				wildDrawPlayerId);
			
			if (wildDrawPlayer == null) {
				throw new GameException("无法找到出+4的玩家");
			}
			
			// 获取质疑前的顶牌颜色
			Color originalColor = getOriginalTopCardColor(roomState);
			
			// 检查出+4的玩家是否有其他可出的牌（与顶牌颜色相同的牌）
			boolean hadPlayableCard = hasPlayableCardOfColor(
				wildDrawPlayer,
				originalColor);
			
			if (hadPlayableCard) {
				// 质疑成功：出+4的玩家确实有其他可出的牌
				// 出+4的玩家需要抽4张牌（原4张+累计加牌）
				for (
					int i = 0;
					i < roomState.getPendingDraw();
					i++) {
					drawCardFromDeck(
						roomState,
						wildDrawPlayer);
				}
			} else {
				// 质疑失败：出+4的玩家确实没有其他可出的牌
				// 质疑者需要抽6张牌（原4张+惩罚2张+累计加牌）
				Player challenger = getPlayerById(
					roomState,
					challengerId);
				if (challenger == null) {
					throw new GameException("无法找到质疑者");
				}
				for (
					int i = 0;
					i < roomState.getPendingDraw() + 2;
					i++) {
					drawCardFromDeck(
						roomState,
						challenger);
				}
			}
		} else {
			// 玩家选择不质疑，直接抽牌
			Player challenger = getPlayerById(
				roomState,
				challengerId);
			if (challenger == null) {
				throw new GameException("无法找到质疑者");
			}
			// 抽取待抽牌数+2张惩罚牌
			for (
				int i = 0;
				i < roomState.getPendingDraw();
				i++) {
				drawCardFromDeck(
					roomState,
					challenger);
			}
		}
		
		// 清空质疑状态
		roomState.setAwaitingChallenge(false);
		roomState.setLastWildDrawPlayer(null);
		
		// 清空待抽牌
		roomState.setPendingDraw(0);
		roomState.setDrawType(null);
		
		return roomState;
	}
	
	
	/**
	 * 检查玩家是否拥有指定颜色的可出牌 用于质疑+4牌时判断出牌玩家是否真的没有其他可出的牌
	 *
	 * @param wildDrawPlayer 出+4牌的玩家
	 * @param originalColor  原始顶牌的颜色
	 *
	 * @return 如果玩家有指定颜色或其他可出的牌返回true，否则返回false
	 */
	private boolean hasPlayableCardOfColor(
		Player wildDrawPlayer,
		Color originalColor) {
		LinkedHashSet<Card> cards = wildDrawPlayer.getCards();
		// 遍历玩家的所有手牌
		for (Card card : cards) {
			// 如果有以下情况之一，则认为玩家有可出的牌：
			// 1. 手牌中有与原始顶牌颜色相同的牌
			// 2. 手牌中有+4万能牌
			// 3. 手牌中有变色牌
			if (card.getColor() == originalColor || card.getType() == Type.WILD_DRAW_4 || card.getType() == Type.WILD) {
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * 获取弃牌堆中最近一张非万能牌的颜色 用于质疑+4牌时判断出牌玩家是否真的没有其他可出的牌
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 *
	 * @return 最近一张非万能牌的颜色，如果找不到则返回null
	 */
	private Color getOriginalTopCardColor(RoomState roomState) {
		ArrayList<Card> discard = roomState.getDiscard();
		// 从弃牌堆顶部开始向下查找，找到第一张非万能牌
		for (
			int i = discard.size();
			i > 0;
			i--) {
			// 检查牌的颜色是否不是万能色
			if (! discard
				.get(i - 1)
				.getColor()
				.equals(Color.WILD)) {
				// 返回找到的第一张非万能牌的颜色
				return discard
					.get(i - 1)
					.getColor();
			}
		}
		// 如果弃牌堆中都是万能牌或者为空，返回null
		return null;
	}
	
	
	/**
	 * 初始化游戏 生成完整的牌组，并给每个玩家分发初始手牌（每人7张）
	 *
	 * @param roomState 房间状态对象，包含游戏的当前状态信息
	 *
	 * @return 初始化完成的房间状态对象
	 */
	@Override
	public RoomState initializeGame(
		RoomState roomState,
		int defaultCreateNumber) {
		// 初始化牌组，生成112张UNO牌
		ArrayList<Card> preDeck = new ArrayList<>(generateCards(defaultCreateNumber));
		// 给每个玩家分发初始手牌
		for (
			int i = 0;
			i < roomState
				.getPlayers()
				.size();
			i++) {
			// 每个玩家初始抽取7张牌
			for (
				int j = 0;
				j < 7;
				j++) {
				// 将牌堆顶的牌添加到玩家手牌末尾
				roomState
					.getPlayers()
					.get(i)
					.getCards()
					.addLast(preDeck.getLast());
				// 从牌堆中移除已分发的牌
				preDeck.removeLast();
			}
		}
		// 设置剩余的牌作为游戏牌堆
		roomState.setDeck(preDeck);
		return roomState;
	}
	
	
	/**
	 * 检查游戏是否结束 通过检查当前玩家是否已经出完所有手牌来判断游戏是否结束
	 *
	 * @param player 出牌玩家
	 *
	 * @return 如果当前玩家手中没有剩余卡牌则返回true（游戏结束），否则返回false（游戏继续）
	 */
	@Transactional
	@Override
	public boolean checkGameEnd(Player player) {
		// 如果玩家手中没有剩余卡牌，则返回true，意味着该玩家胜利，游戏结束
		return player
			.getCards()
			.isEmpty();
	}
	
	
	/**
	 * 生成n张UNO牌，其中数字牌占90%，跳过牌占2%，加2牌占2%，反转牌占2%，万能牌占2%，加4牌占2%。 跳过牌、加2牌、反转牌不能为黑色，万能牌和加4牌只能为黑色，数字牌的颜色和数字大小要均匀分布。
	 *
	 * @param n 要生成的牌的数量
	 *
	 * @return 生成的牌列表
	 */
	private List<Card> generateCards(int n) {
		List<Card> cards = new ArrayList<>();
		
		// 计算每种牌的数量
		int numberCardsCount = (int) (n * 0.9);  // 数字牌占90%
		int skipCardsCount = (int) (n * 0.02);   // 跳过牌占2%
		int draw2CardsCount = (int) (n * 0.02);  // 加2牌占2%
		int reverseCardsCount = (int) (n * 0.02); // 反转牌占2%
		int wildCardsCount = (int) (n * 0.02);    // 万能牌占2%
		int wildDraw4CardsCount = (int) (n * 0.02); // 加4牌占2%
		
		// 确保总数等于n
		int total = numberCardsCount + skipCardsCount + draw2CardsCount + reverseCardsCount + wildCardsCount + wildDraw4CardsCount;
		if (total < n) {
			// 如果总数不足，将差值加到数字牌上
			numberCardsCount += (n - total);
		}
		
		// 生成数字牌（颜色和数字均匀分布）
		Color[] colors = {Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE};
		Number[] numbers = {Number.ZERO, Number.ONE, Number.TWO, Number.THREE, Number.FOUR, Number.FIVE, Number.SIX, Number.SEVEN, Number.EIGHT, Number.NINE};
		
		for (
			int i = 0;
			i < numberCardsCount;
			i++) {
			Card card = new Card();
			card.setId("card_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.NUMBER);
			// 均匀分布颜色
			card.setColor(colors[i % colors.length]);
			// 均匀分布数字
			card.setNumber(numbers[i % numbers.length]);
			cards.add(card);
		}
		
		// 生成跳过牌（不能为黑色）
		for (
			int i = 0;
			i < skipCardsCount;
			i++) {
			Card card = new Card();
			card.setId("skip_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.SKIP);
			card.setColor(colors[i % colors.length]); // 只使用红、绿、黄、蓝
			cards.add(card);
		}
		
		// 生成加2牌（不能为黑色）
		for (
			int i = 0;
			i < draw2CardsCount;
			i++) {
			Card card = new Card();
			card.setId("draw2_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.DRAW_2);
			card.setColor(colors[i % colors.length]); // 只使用红、绿、黄、蓝
			cards.add(card);
		}
		
		// 生成反转牌（不能为黑色）
		for (
			int i = 0;
			i < reverseCardsCount;
			i++) {
			Card card = new Card();
			card.setId("reverse_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.REVERSE);
			card.setColor(colors[i % colors.length]); // 只使用红、绿、黄、蓝
			cards.add(card);
		}
		
		// 生成万能牌（只能为黑色）
		for (
			int i = 0;
			i < wildCardsCount;
			i++) {
			Card card = new Card();
			card.setId("wild_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.WILD);
			card.setColor(Color.WILD); // 只使用黑色
			cards.add(card);
		}
		
		// 生成加4牌（只能为黑色）
		for (
			int i = 0;
			i < wildDraw4CardsCount;
			i++) {
			Card card = new Card();
			card.setId("wild_draw4_" + System.currentTimeMillis() + "_" + i);
			card.setType(Type.WILD_DRAW_4);
			card.setColor(Color.WILD); // 只使用黑色
			cards.add(card);
		}
		
		// 打乱牌堆
		Collections.shuffle(
			cards,
			new Random(System.currentTimeMillis()));
		return cards;
	}
	
	
}
